﻿using Microsoft.AspNetCore.Http.Connections;
using Microsoft.AspNetCore.SignalR.Client;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;

namespace Devonline.Communication.Messages;

/// <summary>
/// 消息通讯器通讯器
/// </summary>
/// <typeparam name="T"></typeparam>
public abstract class SignalCommunicator<T> : Communicator<T>, ICommunicator<T>
{
    #region 内部成员
    private const string LOG_NOT_RUNNING = "Communicator now was not running";
    protected const string LOG_CONNECTION = LOG_COMMUNICATOR + " in state {connectionState}";
    protected const string LOG_SEND_INFO = LOG_COMMUNICATOR + " will call the method {method}";
    protected const string LOG_SEND_INFO_WITH_DATA = LOG_SEND_INFO + " to send the data: {data}";
    protected const string LOG_RECEIVE_INFO = LOG_COMMUNICATOR + " trigger the method {method}";
    protected const string LOG_RECEIVE_INFO_WITH_DATA = LOG_RECEIVE_INFO + " and received the data: {data}";
    protected const string METHOD_SEND = "Send";
    protected readonly MessageOptions _clientSetting;
    protected HubConnection? _connection;
    #endregion

    #region 公开成员
    /// <summary>
    /// IM 服务器地址
    /// </summary>
    public string Host => _clientSetting.Host;
    /// <summary>
    /// 客户端用户编号
    /// </summary>
    public string User => _clientSetting.User;
    /// <summary>
    /// 客户端编号
    /// </summary>
    public string? Client => _clientSetting.Client;
    /// <summary>
    /// 当前连接状态
    /// </summary>
    public override bool IsConnected => _connection?.State == HubConnectionState.Connected;
    #endregion

    public SignalCommunicator(ILogger<SignalCommunicator<T>> logger, MessageOptions clientSetting) : base(logger, clientSetting)
    {
        _clientSetting = clientSetting;
    }

    #region 外部使用的公开方法
    /// <summary>
    /// 启动通讯器
    /// </summary>
    /// <returns></returns>
    public override async Task StartAsync()
    {
        LogConnection("connecting");
        await base.StartAsync();
    }
    /// <summary>
    /// 关闭通讯器
    /// </summary>
    public override async Task StopAsync()
    {
        if (_connection == null)
        {
            return;
        }

        try
        {
            _logger.LogWarning(LOG_COMMUNICATOR + " will stop now", _options);
            _connection.Remove(nameof(Abort));
            _connection.Remove(nameof(Receive));
            _connection.Closed -= OnReconnectingAsync;
            _connection.Reconnected -= OnReconnectedAsync;
            _connection.Reconnecting -= OnReconnectingAsync;
            await _connection.StopAsync();
            await _connection.DisposeAsync();
            await base.StopAsync();
        }
        catch (Exception ex)
        {
            LogConnectionError(ex, "close");
            OnError(ex);
        }
    }
    /// <summary>
    /// 发送数据到默认接收者
    /// 此处的数据对象可以接受 IEnumerable<T> 集合数据, 及 PagedResult<T> 传递分页的数据
    /// </summary>
    /// <param name="t">待发送的数据</param>
    /// <returns></returns>
    public override async Task SendAsync(T t) => await SendAsync(METHOD_SEND, t);
    #endregion

    #region 可继承的内部方法
    /// <summary>
    /// 初始化通讯器
    /// </summary>
    protected override void OnInitial()
    {
        base.OnInitial();

        var connectionUrl = new ConnectionUrl(_clientSetting.User, _clientSetting.Client, _clientSetting.Receiver);
        var url = Host + AppSettings.CHAR_QUESTION + connectionUrl.ToKeyValuePairs().ToString(AppSettings.URL_OUTER_SPLITER, AppSettings.URL_INNER_SPLITER);
        var builder = new HubConnectionBuilder().WithUrl(url, HttpTransportType.WebSockets | HttpTransportType.LongPolling);
        _ = _clientSetting.TransportType switch
        {
            TransportType.Json => builder.AddJsonProtocol(config => config.PayloadSerializerOptions = AppSettings.JsonSerializerOptions),
            TransportType.MessagePack => builder.AddMessagePackProtocol(config => config.SerializerOptions = MessagePack.MessagePackSerializerOptions.Standard),
            _ => builder.AddNewtonsoftJsonProtocol(config => config.PayloadSerializerSettings = AppSettings.JsonSerializerSettings)
        };

        _connection = builder.WithAutomaticReconnect().Build();

        //注册重连和关闭方法
        _connection.Reconnecting += OnReconnectingAsync;
        _connection.Reconnected += OnReconnectedAsync;
        _connection.Closed += OnReconnectingAsync;

        //注册接收消息
        OnHandler(nameof(Receive), OnReceive);

        //注册强制退出
        OnHandler(nameof(Abort), async () => await StopAsync());
    }
    /// <summary>
    /// 连接服务器
    /// </summary>
    /// <returns></returns>
    protected override async Task OpenAsync()
    {
        ArgumentNullException.ThrowIfNull(_connection);
        if (_connection.State == HubConnectionState.Disconnected)
        {
            await _connection.StartAsync();
        }
    }
    /// <summary>
    /// 当链接关闭时执行的事件处理委托方法
    /// </summary>
    /// <param name="exception"></param>
    /// <returns></returns>
    protected virtual async Task OnReconnectingAsync(Exception? exception)
    {
        try
        {
            if (exception != null)
            {
                OnError(exception);
                LogConnectionError(exception, "disconnected");
            }

            await Task.Run(OnConnecting);
        }
        catch (Exception ex)
        {
            OnError(ex);
        }
    }
    /// <summary>
    /// 当建立连接时执行的事件处理委托
    /// </summary>
    /// <param name="id"></param>
    /// <returns></returns>
    protected virtual Task OnReconnectedAsync(string? id) => Task.Run(OnConnected);

    /// <summary>
    /// 注册客户端方法, 接收来自服务器的无参调用
    /// </summary>
    /// <param name="methodName"></param>
    /// <param name="action"></param>
    protected virtual void OnHandler(string methodName, Action action)
    {
        ArgumentNullException.ThrowIfNull(_connection);
        _connection.Remove(methodName);
        _connection.On(methodName, () =>
        {
            if (IsRunning && IsConnected)
            {
                try
                {
                    _logger.LogDebug(LOG_RECEIVE_INFO, _options, methodName);
                    action?.Invoke();
                }
                catch (Exception ex)
                {
                    _logger.LogWarning(ex, LOG_RECEIVE_INFO + " throw exception", _options, methodName);
                    OnError(ex);
                }
            }
        });
    }
    /// <summary>
    /// 注册客户端方法, 接收来自服务器的带参调用
    /// </summary>
    /// <param name="methodName"></param>
    /// <param name="action"></param>
    protected virtual void OnHandler(string methodName, Action<T> action)
    {
        ArgumentNullException.ThrowIfNull(_connection);
        _connection.Remove(methodName);
        _connection.On<T>(methodName, t =>
        {
            if (IsRunning && IsConnected)
            {
                try
                {
                    _logger.LogDebug(LOG_RECEIVE_INFO_WITH_DATA, _options, methodName, t);
                    action?.Invoke(t);
                }
                catch (Exception ex)
                {
                    _logger.LogWarning(ex, LOG_RECEIVE_INFO_WITH_DATA + " throw exception", _options, methodName, t);
                    OnError(ex);
                }
            }
        });
    }

    /// <summary>
    /// 调用服务器无参无返回值的执行方法
    /// </summary>
    /// <param name="methodName"></param>
    /// <returns></returns>
    protected virtual async Task SendAsync(string methodName)
    {
        try
        {
            if (!IsRunning)
            {
                throw new Exception(LOG_NOT_RUNNING);
            }

            if (!IsConnected)
            {
                await OnStartAsync();
            }

            ArgumentNullException.ThrowIfNull(_connection);
            _logger.LogDebug(LOG_SEND_INFO, _options, methodName);
            await _connection.SendAsync(methodName);
        }
        catch (Exception ex)
        {
            _logger.LogWarning(ex, LOG_SEND_INFO + " throw exception", _options, methodName);
            OnError(ex);
        }
    }
    /// <summary>
    /// 调用服务器无返回值的执行方法
    /// </summary>
    /// <param name="methodName"></param>
    /// <param name="t"></param>
    /// <returns></returns>
    protected virtual async Task SendAsync(string methodName, T t)
    {
        try
        {
            if (!IsRunning)
            {
                throw new Exception(LOG_NOT_RUNNING);
            }

            if (!IsConnected)
            {
                await OnStartAsync();
            }

            ArgumentNullException.ThrowIfNull(_connection);
            _logger.LogDebug(LOG_SEND_INFO_WITH_DATA, _options, methodName, t);
            await _connection.SendAsync(methodName, t);
            OnSend(t);
        }
        catch (Exception ex)
        {
            _logger.LogWarning(ex, LOG_SEND_INFO_WITH_DATA + " throw exception", _options, methodName, t);
            OnError(ex);
        }
    }
    /// <summary>
    /// 调用服务器无参有返回值的执行方法
    /// </summary>
    /// <param name="methodName"></param>
    /// <returns></returns>
    protected virtual async Task<T?> InvokeAsync(string methodName)
    {
        try
        {
            if (!IsRunning)
            {
                throw new Exception(LOG_NOT_RUNNING);
            }

            if (!IsConnected)
            {
                await OnStartAsync();
            }

            ArgumentNullException.ThrowIfNull(_connection);
            _logger.LogDebug(LOG_SEND_INFO, _options, methodName);
            return await _connection.InvokeAsync<T>(methodName);
        }
        catch (Exception ex)
        {
            _logger.LogWarning(ex, LOG_SEND_INFO + " throw exception", _options, methodName);
            OnError(ex);
            return default;
        }
    }
    /// <summary>
    /// 调用服务器返回值的执行方法
    /// </summary>
    /// <param name="methodName"></param>
    /// <param name="t"></param>
    /// <returns></returns>
    protected virtual async Task<T?> InvokeAsync(string methodName, T t)
    {
        try
        {
            if (!IsRunning)
            {
                throw new Exception(LOG_NOT_RUNNING);
            }

            if (!IsConnected)
            {
                await OnStartAsync();
            }

            ArgumentNullException.ThrowIfNull(_connection);
            _logger.LogDebug(LOG_SEND_INFO_WITH_DATA, _options, methodName, t);
            var result = await _connection.InvokeAsync<T>(methodName, t);
            OnSend(t);
            return result;
        }
        catch (Exception ex)
        {
            _logger.LogWarning(ex, LOG_SEND_INFO_WITH_DATA + " throw exception", _options, methodName, t);
            OnError(ex);
            return default;
        }
    }
    #endregion

    #region 内部私有成员
    /// <summary>
    /// 记录连接状态日志
    /// </summary>
    /// <param name="connectionState"></param>
    private void LogConnection(string connectionState) => _logger.LogWarning(LOG_CONNECTION, _options, connectionState);
    /// <summary>
    /// 记录连接状态日志
    /// </summary>
    /// <param name="exception"></param>
    /// <param name="connectionState"></param>
    private void LogConnectionError(Exception? exception, string connectionState) => _logger.LogError(exception, LOG_CONNECTION + " throw exception", _options, connectionState);
    /// <summary>
    /// url connector
    /// </summary>
    private record ConnectionUrl(string? User, string? Client, string? Receiver)
    {
        /// <summary>
        /// 重写的转字符串方法返回 url 字符串
        /// </summary>
        /// <returns></returns>
        public override string? ToString() => $"user {User} in client {Client} to receiver {Receiver}";
    }
    #endregion
}